package main

import (
	"fmt"
	"sync"
	"time"
)

// 只定义无需赋值
var wg sync.WaitGroup

func main() {

	//管道本质就是一个数据结构-队列，数据是先进先出
	//自身线程安全，多协程访问时，不需要加锁，channel本身就是线程安全的
	//管道有类型的，一个string的管道只能存放string类型数据
	// 管道定义语法结构：var  变量名  chan  数据类型 ------chan：关键字
	fmt.Println("管道使用=======================================================================重要")
	fmt.Println()
	//定义管道 、 声明管道 ---> 定义一个int类型的管道
	var intChan chan int
	//通过make初始化：管道可以存放3个int类型的数据
	intChan = make(chan int, 3)

	//管道是引用类型：
	fmt.Printf("intChan的值：%v", intChan)
	fmt.Println()

	//向管道存放数据 直接存放 <-
	intChan <- 10
	num := 20
	intChan <- num
	intChan <- 40
	fmt.Printf("存放数据后管道的实际长度：%v,管道的容量是：%v", len(intChan), cap(intChan))

	//不能存放大于容量的数据 现在管道容量是3 make初始化的时候定义的  intChan<- 80
	//内置函数close可以关闭管道，当管道关闭后，就不能再向管道写数据了，但是仍然可以从该管道读取数据。
	close(intChan)
	// 再次写入数据 --> 报错 intChan <- 30

	//读取管道数据 读取数据 := <-
	num1 := <-intChan
	num2 := <-intChan
	num3 := <-intChan
	fmt.Println(num1)
	fmt.Println(num2)
	fmt.Println(num3)

	//注意：在没有使用协程的情况下，如果管道的数据已经全部取出，那么再取就会报错：
	//num4 := <-intChan
	fmt.Printf("取完数据后管道的实际长度：%v,管道的容量是：%v", len(intChan), cap(intChan))
	fmt.Println()

	fmt.Println("管道遍历=======================================================================重要")
	fmt.Println()

	// 定义管道
	intChan1 := make(chan int, 60)
	for i := 0; i < 60; i++ {
		intChan1 <- i
	}

	//在遍历前，如果没有关闭管道，就会出现deadlock的错误
	//所以我们在遍历前要进行管道的关闭
	close(intChan1)

	// 只能使用for-range来遍历管道
	for value := range intChan1 {
		fmt.Println("使用for-range循环遍历结果value = ", value)
	}

	fmt.Println()

	fmt.Println("协程配合管道遍历=======================================================================重要")
	fmt.Println()
	//写协程和读协程共同操作同一个管道-》定义管道：
	intchan2 := make(chan int, 10)
	wg.Add(2)
	//开启写和读的协程：
	go writeData(intchan2)
	go readData(intchan2)
	//主线程一直在阻塞，什么时候wg减为0了，就停止
	wg.Wait()
	fmt.Println()

	fmt.Println("只写或者只读管道=======================================================================重要")
	fmt.Println()
	//声明为只写 管道具备<- 只写性质
	var intchan3 chan<- int
	intchan3 = make(chan int, 2)
	intchan3 <- 10
	fmt.Println(intchan3)
	//声明为只读： 管道具备<- 只读性质
	var intchan4 <-chan int
	if intchan4 != nil {
		num1 := <-intchan4
		fmt.Println("num1:", num1)
	}
	fmt.Println()

	fmt.Println("用refer+recover捕获panic进行处理======================================================================重要")
	fmt.Println()

	//启动两个协程：
	wg.Add(2)
	go printNum()
	go devide()
	wg.Wait()
}

// 写
func writeData(intChan chan int) {
	defer wg.Done()
	for i := 0; i < 3; i++ {
		intChan <- i
		fmt.Println("写入的数据为：", i)
		time.Sleep(time.Second)
	}
	//管道关闭：
	close(intChan)
}

// 读
func readData(intChan chan int) {
	defer wg.Done()
	for v := range intChan {
		fmt.Println("读取的数据为：", v)
		time.Sleep(time.Second)
	}
}

// 输出数字：
func printNum() {
	wg.Done()
	for i := 1; i <= 10; i++ {
		fmt.Println(i)
	}
}

// 做除法操作：
func devide() {
	defer wg.Done()
	// 利用defer+recover来捕获异常，defer后加上匿名函数的调用
	defer func() {
		// 调用recover内置函数。可以捕获异常
		err := recover()
		// 如果没有捕获异常，返回值为零之：nil
		if err != nil {
			fmt.Println("devide()出现错误：", err)
			fmt.Println("err：", err)
		}
	}()
	num1 := 10
	num2 := 0
	result := num1 / num2
	fmt.Println(result)
}
